#include <qplastiquestyle.h>
#include "vxruller.h"


/*
 *
 *
 * VxRullers
 *
 *
 */


VxRuller::VxRuller(QGraphicsView * view, bool controls) :
		QFrame(), _orientation(CENTER), _view(view),
		_snap(SNAP_TO_FRONT), _info(new RullersInfo())
{

	_createGUI();

	VxRuller * hruller = new VxRuller(_view, VxRuller::HORIZONTAL, _info);
	VxRuller * vruller = new VxRuller(_view, VxRuller::VERTICAL, _info);
	hruller->stackUnder(this);
	vruller->stackUnder(this);

	_info->center = this;
	_info->vruller = vruller;
	_info->hruller = hruller;

	hruller->installEventFilter(this);
	vruller->installEventFilter(this);


	VxPinButton * pinButton = NULL;
	if (controls){
		pinButton = new VxPinButton(NULL, VxPinButton::PINNED);
		hruller->addControlButton(pinButton);
	}

}


VxRuller::VxRuller(QGraphicsView * view, Orientation orientation, RullersInfo * info) :
		QFrame(), _orientation(orientation), _view(view),
		_snap(SNAP_TO_FRONT), _mode(MOVEMENT), _info(info)
{
	_createGUI();

	_view->installEventFilter(this);
	qApp->installEventFilter(this);

}


VxRuller::~VxRuller()
{
}



bool VxRuller::eventFilter(QObject *obj, QEvent *event)
{
	if (event->type() == QEvent::Move ||
		event->type() == QEvent::Resize) {
		_adjustRullers();
	} else
	if (event->type() == QEvent::MouseMove){
		QMouseEvent * msevnt = static_cast<QMouseEvent *>(event);
		QWidget * widget =  static_cast<QWidget *>(obj);
		QPoint p =  msevnt->pos() + widget->geometry().topLeft();


		_cursorMove(p);
	}

	return QObject::eventFilter(obj, event);
}


void VxRuller::_cursorMove(QPoint mpoint)
{
	QRect r = _info->center->geometry();
	switch(_orientation){

	case HORIZONTAL:
		if (mpoint.x() < r.left() || mpoint.x() > r.right()){
			_pointer->move(mpoint.x()-2, 0);
			if (!_pointer->isVisible()){
				_pointer->show();
			}
		} else {
			_pointer->hide();
		}
		break;

	case VERTICAL:
		if (mpoint.y() < r.top() || mpoint.y() > r.bottom()){
			_pointer->move(0, mpoint.y()-2);
			if (!_pointer->isVisible()){
				_pointer->show();
			}
		} else {
			_pointer->hide();
		}
		break;

	default:;
	}

}


void VxRuller::paintEvent(QPaintEvent * event)
{
	QFrame::paintEvent(event);
	
	QStyleOptionFrame * options = new QStyleOptionFrame();
	options->initFrom(this);


	QPainter * painter = new QPainter(this);

	painter->setFont(QFont("Verdana", 7));

	_setClipRect(painter);

	if (_orientation != CENTER){
		QPen pen = painter->pen();
		painter->setPen(pen);
		_drawScale(painter, options);
	}

	delete painter;
}



void VxRuller::_resizeView(VxGraphicsView *)
{
	_adjustRullers();
}


void VxRuller::_setClipRect(QPainter * painter)
{
	if (!_clipPath.isEmpty()){
		painter->setClipPath(_clipPath, Qt::ReplaceClip);
	}

}


void VxRuller::_snapSwitched(Snap snap)
{
	_adjustRullers();
}



void VxRuller::_adjustRullers()
{
	switch (_orientation){
	case CENTER: {
			QRect hr = _info->hruller->geometry();
			QRect vr = _info->vruller->geometry();
			QRect * cr = new QRect(vr.left(), hr.top(), vr.width(), hr.height());
			this->setGeometry(*cr);
			break;
		}

	case HORIZONTAL:{
			QHBoxLayout * hlayout = static_cast<QHBoxLayout *>(this->layout());
			
			switch (_info->vruller->snap()){
				
			case VxRuller::SNAP_TO_FRONT:
				hlayout->setDirection(QBoxLayout::LeftToRight);
				break;
				
				
			case VxRuller::SNAP_TO_BACK:
				hlayout->setDirection(QBoxLayout::RightToLeft);
				break;
				
			default:;
			}
			QPoint p = _validateXY(x(), y());
			move(p.x(), p.y());
		}

	case VERTICAL:{
			QPoint p = _validateXY(x(), y());
			move(p.x(), p.y());
			break;
		}
	}
	if (layout()){
		layout()->update();
	}
}


QRect VxRuller::getBoundingRect() const
{
	return _view->viewport()->geometry();
}


void VxRuller::_drawScale(QPainter * painter, QStyleOptionFrame * options)
{

	QPointF p = _view->mapToScene(this->x(), this->y());

	qreal lT0 = (_orientation == HORIZONTAL) ? p.x() : p.y();

	qreal scale = _view->matrix().m11();

	qreal mu = pow(10.0, floor(log10(526/scale)));

	qreal aT0 = ((int)(lT0/mu))*mu;

	aT0 -= (lT0 < 0 ? mu: 0);

	qreal aStep = mu/10;
	qreal rs = aStep*scale;
	int ns = (int)(rs/10);
	if (ns > 1){
		ns = (ns%2 == 1)? (ns-1) : ns;
		aStep /= ns;
	}

	int aI = 0;

	qreal x = 0;
	qreal xn = (_orientation == HORIZONTAL)? this->width() : this->height();

	do{
		qreal aV = aT0 + aI*aStep;
		x = (aV - lT0)*scale;
		if (x >= 0) {

			switch (_orientation){

			case HORIZONTAL:
					_drawMetricHorz(painter, x, aI, aV);
					break;

				case VERTICAL:
					_drawMetricVert(painter, x, aI, aV);
					break;

				case CENTER:
					break;
			}
		}
		aI++;
	} while (x <= xn);

}


void VxRuller::_drawMetricHorz(QPainter * painter, qreal position, int aI, qreal aV)
{
	qreal y = 0;
	int h = height();

	if ((position >= 0) && (position < width())){
		if (aI%10 == 0){
			y = 0;


			if (_snap != SNAP_TO_BACK){
				painter->drawText(QPoint(position+1, 9), QString("%0").arg(aV));
			} else {
				painter->drawText(QPoint(position+1, 16), QString("%0").arg(aV));
			}


		} else {
			if (aI%5 == 0){
				y = h*0.6;
			} else {
				y = h*0.8;
			}
		}

		if (_snap != SNAP_TO_BACK){
			painter->drawLine(position, h, position,y);
		} else {
			painter->drawLine(position, h-y, position,0);
		}
	}
}


void VxRuller::_drawMetricVert(QPainter * painter, qreal position, int aI, qreal aV)
{
	qreal x = 0;
	int w = width();

	if ((position >= 0) && (position < height())){
		if (aI%10 == 0){
			x = 0;

			painter->rotate(-90);
			if (_snap != SNAP_TO_BACK){
				painter->drawText(QPoint(-position + 1, 9), QString("%0").arg(-aV));
			} else {
				painter->drawText(QPoint(-position + 1, 16), QString("%0").arg(-aV));
			}
			painter->rotate(90);


		} else {
			if (aI%5 == 0){
				x = w*0.6;
			} else {
				x = w*0.8;
			}
		}

		if (_snap != SNAP_TO_BACK){
			painter->drawLine(w, position, x, position);
		} else {
			painter->drawLine(w-x, position, 0, position);
		}
	}
}


QPoint VxRuller::_validateXY(qreal vx, qreal vy)
{
	QRect br = getBoundingRect();

	switch(_orientation) {

	case HORIZONTAL:

		this->setFixedWidth(br.width());

		switch(_snap){

			case SNAP_TO_BACK:
				vy = br.bottom() - height() + 1;
				break;

			case SNAP_TO_FRONT:
				vy = br.top();
				break;

			default:;
		};

		if (vx != br.left()){
			vx = br.left();
		};

		break;

	case VERTICAL:

		this->setFixedHeight(br.height());

		switch(_snap){

			case SNAP_TO_BACK:
				vx = br.right() - width() + 1;
				break;

			case SNAP_TO_FRONT:
				vx = br.left();
				break;

			default:;

		};

		if (vy != br.top()){
			vy = br.top();
		};

		break;

	case NO_SNAP:;

	}

	if (_snap == NO_SNAP){
		int w = br.width() - HEIGHT + 2;
		int h = br.height() - HEIGHT + 2;

		if (vx < br.left()){
			vx = br.left();
		} else
		if (vx > w){
			vx = w;
		}
		if (vy < br.top()){
			vy = br.top();
		} else
		if (vy > h){
			vy = h;
		}
	}

	return QPoint(vx, vy);

}



void VxRuller::_setMode(Mode mode)
{
	if (_mode == mode){
		return;
	}

	_mode = mode;

	emit modeSwitched(_mode);

	switch(_mode){

	case MOVEMENT:
		emit switchedMovementMode();
		break;

	case STANDING:
		emit switchedGuidsMode();
		break;

	}
}


void VxRuller::setSnap(Snap snap)
{
	if (snap == _snap){
		return;
	}

	_snap = snap;
	//_adjustRullers();

	emit snapSwitched(snap);

}




QState * VxRuller::buildStandingState()
{
	QState * state = new QState();
	state->assignProperty(this, "mode", STANDING);
	return state;
}



void VxRuller::addControlButton(QWidget * control)
{
	layout()->addWidget(control);
}


void VxRuller::_createGUI()
{
	setObjectName("vxRuller");
	setAttribute(Qt::WA_TransparentForMouseEvents);

	switch(_orientation){

	case CENTER:{

			QStackedLayout * layout = new QStackedLayout(this);

			QWidget * wmovement = new QWidget();
			QWidget * wstanding = new QWidget();

			layout->addWidget(wmovement);
			QLabel * labelMovement = new QLabel(wmovement);
			labelMovement->setPixmap(QPixmap(":/images/rullers_movement.png"));
			labelMovement->move(3,3);

			layout->addWidget(wstanding);
			QLabel * labelGuids = new QLabel(wstanding);
			labelGuids->setPixmap(QPixmap(":/images/rullers_guids.png"));
			labelGuids->move(3,3);
			connect(this, SIGNAL(modeSwitched(int)), layout, SLOT(setCurrentIndex(int)));

			break;

		}

	case HORIZONTAL:{

			this->setFixedHeight(HEIGHT);

			QHBoxLayout * hlayout = new QHBoxLayout();
			hlayout->setSpacing(0);

			hlayout->setContentsMargins(0,0,0,0);
			setLayout(hlayout);
			hlayout->addStretch();

			_createPointer();
			break;
		}

	case VERTICAL:{

			this->setFixedWidth(HEIGHT);
			_createPointer();
			break;
		}

	}


}



void VxRuller::_createPointer()
{
	_pointer = new QWidget(this);
	_pointer->setAutoFillBackground(true);
	_pointer->setAttribute(Qt::WA_TransparentForMouseEvents);

	QPalette palette = _pointer->palette();
	palette.setColor(_pointer->backgroundRole(), QColor(0,0,255, 80));
	_pointer->setPalette(palette);

	switch(_orientation){

	case HORIZONTAL:
		_pointer->setFixedSize(1, HEIGHT);
		_pointer->move(0,0);
		break;

	case VERTICAL:
		_pointer->setFixedSize(HEIGHT, 1);
		_pointer->move(0,0);
		break;

	default:;
	}

}

/*
 *
 *
 * VxRullersProxyWidget
 *
 *
 */


void VxRullersProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
	VxProxyWidget::paint(painter, option, widget);

	if (_alreadyMoving){
		return;
	}

	_updateCrossPosition();

}

VxRullersProxyWidget * VxRullersProxyWidget::createInstance(VxGraphicsView * view)
{
	VxRuller * _rullers = new VxRuller(view, true);

	VxProxyWidget * horzProxy = new VxProxyWidget(view);
	VxProxyWidget * vertProxy = new VxProxyWidget(view);

	VxRullersProxyWidget * centerProxy = new VxRullersProxyWidget(view, _rullers,
											horzProxy, vertProxy);

	connect(horzProxy->animation(), SIGNAL(finished()), centerProxy, SLOT(_checkSnap()));
	connect(vertProxy->animation(), SIGNAL(finished()), centerProxy, SLOT(_checkSnap()));
	connect(centerProxy->animation(), SIGNAL(finished()), centerProxy, SLOT(_checkSnap()));
	return centerProxy;

}


VxRullersProxyWidget::VxRullersProxyWidget(VxGraphicsView * view, VxRuller * _rullers,
					  VxProxyWidget * hp, VxProxyWidget * vp, QGraphicsItem * parent) :
			VxProxyWidget(view, parent), _horzProxy(hp), _vertProxy(vp), _alreadyMoving(false)
{
	this->setWidget(_rullers);
	this->setZValue(0xffff02);
	view->scene()->addItem(this);

	view->scene()->addItem(_horzProxy);
	view->scene()->addItem(_vertProxy);

	_horzProxy->setWidget(_rullers->horzRuller());
	_horzProxy->setZValue(0xffff01);

	_vertProxy->setWidget(_rullers->vertRuller());
	_vertProxy->setZValue(0xffff01);

	connect(_horzProxy, SIGNAL(movement(int,int)), this, SLOT(movement(int,int)));
	connect(_vertProxy, SIGNAL(movement(int,int)), this, SLOT(movement(int,int)));

	connect(this, SIGNAL(movement(int,int)), this, SLOT(_centerMovement(int,int)));

	_horzProxy->widget()->installEventFilter(this);
	_vertProxy->widget()->installEventFilter(this);

	_view->installEventFilter(this);

}


void VxRullersProxyWidget::_updateCrossPosition()
{
	qreal px = _vertProxy->getViewPos().x();
	qreal py = _horzProxy->getViewPos().y();
	setViewPos(px, py);


	QRectF cr = sceneBoundingRect();

	QPainterPath hcpath;
	QRectF hcr = _horzProxy->mapFromScene(cr).boundingRect();
	hcpath.addRect(0,0, hcr.left(), hcr.height());
	hcpath.addRect(QRectF(hcr.topRight(), _horzProxy->rect().bottomRight()));
	static_cast<VxRuller *>(_horzProxy->widget())->setClipPath(hcpath);

	QPainterPath vcpath;
	QRectF vcr = _vertProxy->mapFromScene(cr).boundingRect();
	vcpath.addRect(0,0, vcr.right(), vcr.top());
	vcpath.addRect(QRectF(vcr.bottomLeft(), _vertProxy->rect().bottomRight()));
	static_cast<VxRuller *>(_vertProxy->widget())->setClipPath(vcpath);

}


void VxRullersProxyWidget::_centerMovement(int dx, int dy)
{
	if (_alreadyMoving){
		return;
	}
	_alreadyMoving = true;
	QPoint cp = getViewPos();
	QPoint hp = _horzProxy->getViewPos();
	_horzProxy->setViewPos(hp.x(), cp.y());
	QPoint vp = _vertProxy->getViewPos();
	_vertProxy->setViewPos(cp.x(), vp.y());
	_alreadyMoving = false;
}



void VxRullersProxyWidget::movement(int, int)
{
	_updateCrossPosition();
}

void _changeSnap(VxProxyWidget * proxy)
{
	VxRuller * ruller = static_cast<VxRuller *>(proxy->widget());
	switch (ruller->snap()){

	case VxRuller::SNAP_TO_FRONT:
		ruller->setSnap(VxRuller::SNAP_TO_BACK);
		break;

	case VxRuller::SNAP_TO_BACK:
		ruller->setSnap(VxRuller::SNAP_TO_FRONT);
		break;

	case VxRuller::NO_SNAP:
		ruller->setSnap(VxRuller::SNAP_TO_FRONT);
		break;
	}

}

bool VxRullersProxyWidget::eventFilter(QObject *object, QEvent *event)
{
	VxProxyWidget::eventFilter(object, event);

	if (object == _horzProxy->widget() || object == _vertProxy->widget() || object == this->widget()){
		switch (event->type()){
		case QEvent::MouseButtonDblClick:{
				VxRuller * ruller = static_cast<VxRuller *>(object);

				switch (ruller->orientation()){

				case VxRuller::HORIZONTAL:
					_changeSnap(_horzProxy);
					_horzProxy->setViewPos(_validatePos(_horzProxy));
					break;

				case VxRuller::VERTICAL:
					_changeSnap(_vertProxy);
					_vertProxy->setViewPos(_validatePos(_vertProxy));
					break;

				case VxRuller::CENTER:
					_changeSnap(_horzProxy);
					_changeSnap(_vertProxy);
					_horzProxy->setViewPos(_validatePos(_horzProxy));
					_vertProxy->setViewPos(_validatePos(_vertProxy));
					break;
				}
				break;
			}
		case QEvent::MouseButtonRelease: {
				_checkSnap();
				break;
			}
		default:;
		}
	} else
	if  (object == _view){
		switch(event->type()){
		case QEvent::Resize: {
			_horzProxy->setViewPos(_validatePos(_horzProxy));
			_vertProxy->setViewPos(_validatePos(_vertProxy));
			break;
		}
		case QEvent::Paint: {
			_horzProxy->setViewPos(_validatePos(_horzProxy));
			_vertProxy->setViewPos(_validatePos(_vertProxy));
			break;
		default:;
		}
	}
	}
	return false;
}


void VxRullersProxyWidget::_validateSnap(VxProxyWidget * proxy)
{
	QPoint pos = proxy->getViewPos();
	QRect br = _view->viewport()->rect();

	VxRuller * ruller = static_cast<VxRuller *>(proxy->widget());

	switch(ruller->orientation()){

	case VxRuller::HORIZONTAL:
		if (pos.y() < VxRuller::SNAP_DELTA){
			proxy->cancelAnimation();
			this->cancelAnimation();
			if (ruller->snap() != VxRuller::SNAP_TO_FRONT){
				ruller->setSnap(VxRuller::SNAP_TO_FRONT);
			}
			proxy->setViewPos(_validatePos(proxy));
		} else
		if (pos.y() > br.bottom() - ruller->height() - VxRuller::SNAP_DELTA){
			proxy->cancelAnimation();
			this->cancelAnimation();
			if (ruller->snap() != VxRuller::SNAP_TO_BACK){
				ruller->setSnap(VxRuller::SNAP_TO_BACK);
			}
			proxy->setViewPos(_validatePos(proxy));
		} else {
			ruller->setSnap(VxRuller::NO_SNAP);
		}
		break;

	case VxRuller::VERTICAL:;
		if (pos.x() < VxRuller::SNAP_DELTA){
			proxy->cancelAnimation();
			this->cancelAnimation();
			if (ruller->snap() != VxRuller::SNAP_TO_FRONT){
				ruller->setSnap(VxRuller::SNAP_TO_FRONT);
			}
			proxy->setViewPos(_validatePos(proxy));
		} else
		if (pos.x() > br.right() - ruller->width() - VxRuller::SNAP_DELTA){
			proxy->cancelAnimation();
			this->cancelAnimation();
			if (ruller->snap() != VxRuller::SNAP_TO_BACK){
				ruller->setSnap(VxRuller::SNAP_TO_BACK);
			}
			proxy->setViewPos(_validatePos(proxy));
		} else {
			ruller->setSnap(VxRuller::NO_SNAP);
		}
		break;


	case VxRuller::CENTER:{
			_checkSnap();
		}
	}

}

QPoint VxRullersProxyWidget::_validatePos(VxProxyWidget * proxy)
{
	QPoint pos = proxy->getViewPos();
	QRect br = _view->viewport()->rect();
	VxRuller * ruller = static_cast<VxRuller *>(proxy->widget());

	switch(ruller->orientation()){
	case VxRuller::HORIZONTAL:
		switch (ruller->snap()){
		case VxRuller::SNAP_TO_FRONT:
			pos.setY(0);
			break;
		case VxRuller::SNAP_TO_BACK:
			pos.setY(br.bottom()-ruller->height()+1);
			break;
		default:;
		}
		break;
	case VxRuller::VERTICAL:
		switch (ruller->snap()){
		case VxRuller::SNAP_TO_FRONT:
			pos.setX(0);
			break;
		case VxRuller::SNAP_TO_BACK:
			pos.setX(br.right()-ruller->width()+1);
			break;
		default:;
		}
		break;

	default:;
	}

	return pos;
}


void VxRullersProxyWidget::cancelAnimation()
{
	VxProxyWidget::cancelAnimation();
}


void VxRullersProxyWidget::_checkSnap()
{
	_validateSnap(_horzProxy);
	_validateSnap(_vertProxy);
}





